package wangchao.validate.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import wangchao.conf.FormatUtil;
import wangchao.tools.StringX;
import wangchao.validate.Constant;
import wangchao.validate.FieldTypeEnum;
import wangchao.validate.ValidateType;
import wangchao.validate.annotation.WangcMethodValidate;
import wangchao.validate.annotation.WangcParamterValidate;
import wangchao.validate.annotation.WangcTypeValidate;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 验证逻辑
 * Created by ashin on 2018/11/1.
 */
public class DataValidate implements DataValidateFilter {

    /**
     * log
     */
    protected static final Logger logger = LoggerFactory.getLogger(DataValidate.class);

    SpecificValidationAdapter specificValidationAdapter = new SpecificValidationAdpterImp();

//    @Override
//    public JSONObject invokeClassValidate(Class<?> clazz) {
//
//        System.out.println("执行 " + clazz.getName() + " 类的验证开始");
//        for (Method method : clazz.getDeclaredMethods()) {
//            WangcMethodValidate wangc = method.getAnnotation(WangcMethodValidate.class);
//            if (wangc != null) {
//                System.out.println("验证 " + method.getName() + " 方法开始");
//                System.out.println("方法 " + method.getName() + " 的参数个数为 " + method.getParameterCount());
//                Parameter[] parameters = method.getParameters();
//                for (Parameter parameter : parameters) {
//                    System.out.println("参数类型为： " + parameter.getType());
//                    System.out.println("参数名称为： " + parameter.getName());
//                }
//                System.out.println("是否验证所有字段是否为空： " + wangc.checkAllParamtersIsNull());
//                System.out.println("一共有 " + wangc.names().length + " 个字段需要特殊处理");
//                //遍历字段
//                for (WangcParamterValidate wangcParamterValidate : wangc.names()) {
//                    System.out.println("字段名称为： " + wangcParamterValidate.name());
//                    System.out.println("一共有 " + wangcParamterValidate.type().length + " 个验证类型需要操作");
//                    //遍历每个字段里面需要操作的类型
//                    for (WangcTypeValidate wangcTypeValidate : wangcParamterValidate.type()) {
//
//                        System.out.println("类型验证为： " + wangcTypeValidate.validate());
//                        System.out.println("类型特殊验证限制为：  " + wangcTypeValidate.specify());
//                        System.out.println("类型正则表达式为： " + wangcTypeValidate.regex());
//                        System.out.println("类型提示语为： " + wangcTypeValidate.msg());
//                    }
//                }
//            }
//
//        }
//        System.out.println("执行 " + clazz.getName() + " 类的验证结束");
//        return null;
//    }


    @Override
    public JSONObject invokeAOP(ProceedingJoinPoint joinPoint) throws Throwable {
        String classType = joinPoint.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        Object[] args = joinPoint.getArgs();
        Map<String, Object> nameAndArgs = getFieldsName(this.getClass(), clazzName, methodName, args);
        JSONObject argsObject = JSONObject.parseObject(JSON.toJSONString(nameAndArgs));
        MethodSignature ms = (MethodSignature) signature;
        Method method = ms.getMethod();
        JSONArray validateArray = new JSONArray();
        //方法验证
        JSONObject validateMethod = invokeMethodValidate(method, argsObject);

        if (validateMethod.getInteger(CODE) == eCode) {
            validateArray.add(validateMethod);
        }

        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            JSONObject validateField = invokeFieldValidate(parameter, parameter.getName(), nameAndArgs.get(parameter.getName()));
            if (validateField.getInteger(CODE) == eCode) {
                validateArray.add(validateField);
            }
        }

        //需要检查合并json
        if (validateArray.size() > 0) {
            return mergeJSON(validateArray);
        }
        FormatUtil.printJson(validateArray.toJSONString());
        JSONObject o = (JSONObject) joinPoint.proceed();
        return o;
    }

    /**
     * 合并json
     *
     * @param validateArray
     * @return
     */
    private JSONObject mergeJSON(JSONArray validateArray) {
        JSONObject _validateJSON = validateArray.getJSONObject(0);
        for (int i = 1; i < validateArray.size(); i++) {
            JSONArray _validates = validateArray.getJSONObject(i).getJSONArray(VALIDATES);
            for (int j = 0; j < _validates.size(); j++) {
                _validateJSON = compareJSON(_validateJSON, _validates.getJSONObject(j));
            }
        }
        return _validateJSON;
    }

    /**
     * 对比json
     *
     * @return
     */
    private JSONObject compareJSON(JSONObject sourceJSON, JSONObject compareJSON) {
        JSONArray validates = sourceJSON.getJSONArray(VALIDATES);
        for (int i = 0; i < validates.size(); i++) {
            JSONObject validate = validates.getJSONObject(i);
            if (validate.getString(KEY).equals(compareJSON.getString(KEY))) {
                if (validate.getString(ERROR_MSG).equals(compareJSON.getString(ERROR_MSG))) {
                    continue;
                } else {
                    validate.put(ERROR_MSG, validate.getString(ERROR_MSG) + ";" + compareJSON.getString(ERROR_MSG));
                }
            }
        }
        return sourceJSON;
    }


    /**
     * 获得字段名字
     *
     * @param cls
     * @param clazzName
     * @param methodName
     * @param args
     * @return
     * @throws NotFoundException
     */
    private Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String, Object> map = new HashMap<String, Object>();

        ClassPool pool = ClassPool.getDefault();
        //ClassClassPath classPath = new ClassClassPath(this.getClass());
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);

        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        if (attr == null) {
            // exception
        }
        // String[] paramNames = new String[cm.getParameterTypes().length];
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            if (!"request".equals(attr.variableName(i + pos))) {
                map.put(attr.variableName(i + pos), args[i]);//paramNames即参数名
            }
        }

        //Map<>
        return map;
    }

    @Override
    public JSONObject invokeMethodValidate(Method method, JSONObject args) {
        JSONObject _args = new JSONObject();
        for (Map.Entry<String, Object> entry : args.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof Map) {
                Iterator entries = ((Map) value).entrySet().iterator();
                while (entries.hasNext()) {
                    Map.Entry<String, Object> _entry = (Map.Entry) entries.next();
                    _args.put(_entry.getKey(), _entry.getValue());
                }
            } else {
                _args.put(entry.getKey(), value);
            }
        }
        logger.info("执行 {} 方法的验证开始", method.getName());
        WangcMethodValidate wangc = method.getAnnotation(WangcMethodValidate.class);
        JSONObject methodJSON = new JSONObject();
        List<JSONObject> jsonObjectList = new ArrayList<>();
        if (wangc != null) {
            logger.info("方法 {} 的参数个数为 {}", method.getName(), method.getParameterCount());
            Parameter[] parameters = method.getParameters();
            if (wangc.excludeCheckRequest()) {
                boolean hasRequest = false;
                List<Parameter> parameterList = new ArrayList<>(Arrays.asList(parameters));
                for (Parameter parameter : parameters) {
                    Class type = parameter.getType();
                    if (type.equals(HttpServletRequest.class)) {
                        hasRequest = true;
                        parameterList.remove(parameter);
                    }
                }
                logger.info("是否包含request入参： {}", hasRequest);
                parameters = parameterList.toArray(new Parameter[parameterList.size()]);
            }

            WangcParamterValidate[] wangcParamterValidateArr = wangc.names();
            //是否所有已传入的参数都不能为空
            if (wangc.checkAllParamtersIsNull()) {
                jsonObjectList = checkJSONObjectNotNull(_args);
            }
            for (WangcParamterValidate wangcParamterValidate : wangcParamterValidateArr) {
                WangcTypeValidate[] wangcTypeValidateArr = wangcParamterValidate.type();
                String fieldName = wangcParamterValidate.name();
                jsonObjectList.addAll(invokeParamterValidate(parameters, fieldName, _args.get(fieldName), wangcTypeValidateArr));
            }
        }
        logger.info("执行 {} 方法的验证结束", method.getName());

        return formMethodJSON(jsonObjectList, methodJSON);
    }

    /**
     * 组装json数据返回
     *
     * @param jsonObjectList
     * @param methodJSON
     * @return
     */
    private JSONObject formMethodJSON(List<JSONObject> jsonObjectList, JSONObject methodJSON) {
        if (jsonObjectList.size() > 0) {
            methodJSON.put(CODE, eCode);
            methodJSON.put(MESS, FAIL);
            methodJSON.put(VALIDATES, jsonObjectList);
        } else {
            methodJSON.put(CODE, sCode);
            methodJSON.put(MESS, SUCCESS);
        }
        return methodJSON;
    }

    /**
     * 验证json每一个元素都不能为null
     *
     * @param jsonObject
     * @return
     */
    private List<JSONObject> checkJSONObjectNotNull(JSONObject jsonObject) {
        logger.info("-------非空验证开始------");
        List<JSONObject> jsonObjectList = new ArrayList<>();
        for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
            JSONObject _json = new JSONObject();
            logger.info("key -> {}，value -> {}", entry.getKey(), entry.getValue());
            if (entry.getValue() == null || StringX.isEmpty(String.valueOf(entry.getValue()))) {
                _json.put(KEY, entry.getKey());
                _json.put(ERROR_MSG, Constant.NOT_NULL);
                jsonObjectList.add(_json);
            }
        }
        logger.info("-------非空验证结束------");
        return jsonObjectList;
    }

    private List<JSONObject> invokeParamterValidate(Parameter[] parameters, String objectKey, Object object, WangcTypeValidate[] wangcTypeValidateArr) {
        List<JSONObject> jsonObjectList = new ArrayList<>();
        for (Parameter parameter : parameters) {
            for (WangcTypeValidate wangcTypeValidate : wangcTypeValidateArr) {
                jsonObjectList.addAll(validateByObjectType(parameter.getType(), wangcTypeValidate, objectKey, object));
            }
        }
        return jsonObjectList;
    }

    @Override
    public JSONObject invokeFieldValidate(Parameter parameter, String objectKey, Object object) {
        logger.info("执行 {} 参数的验证开始", parameter.getName());
        logger.info("参数类型为： {}", parameter.getType());
        logger.info("参数名称为： {}", parameter.getName());
        WangcTypeValidate wangcTypeValidate = parameter.getAnnotation(WangcTypeValidate.class);
        JSONObject fieldValidate = new JSONObject();
        List<JSONObject> objectTypeList = new ArrayList<>();
        if (wangcTypeValidate != null) {
            objectTypeList = validateByObjectType(parameter.getType(), wangcTypeValidate, objectKey, object);
        }
        logger.info("执行 {} 参数的验证结束", parameter.getName());
        return formMethodJSON(objectTypeList, fieldValidate);
    }

    /**
     * 根据入参类型进行验证
     *
     * @param wangcTypeValidate
     * @return
     */
    private List<JSONObject> validateByObjectType(Class paramType, WangcTypeValidate wangcTypeValidate, String objectKey, Object object) {
        String msg = wangcTypeValidate.msg();
        String regex = wangcTypeValidate.regex();
        int specify = wangcTypeValidate.specify();
        FieldTypeEnum fieldTypeEnum = wangcTypeValidate.fieldType();
        ValidateType validateType = wangcTypeValidate.validate();
        List<JSONObject> jsonObjectList = new ArrayList<>();
        jsonObjectList = checkByType(paramType, validateType, regex, specify, msg, objectKey, object, jsonObjectList, fieldTypeEnum);
        return jsonObjectList;
    }

    /**
     * 根据类型进行判断
     *
     * @param validateType
     * @param regex
     * @param specify
     * @param msg
     * @param objectKey
     * @param object
     * @param jsonObjectList
     * @return
     */
    private List<JSONObject> checkByType(Class paramType, ValidateType validateType, String regex, int specify, String msg, String objectKey, Object object, List<JSONObject> jsonObjectList, FieldTypeEnum fieldTypeEnum) {
        if (object instanceof Map) {
            //Map类型
            jsonObjectList = checkByTypeMap(paramType, validateType, regex, specify, msg, objectKey, (Map) object, jsonObjectList, fieldTypeEnum);
        } else if (object instanceof List) {
            //集合
            jsonObjectList = checkByTypeType(paramType, validateType, regex, specify, msg, objectKey, (List) object, jsonObjectList, fieldTypeEnum);
        } else if (object == null || object instanceof String) {
            //字符串
            jsonObjectList = validateStringByValidateType(validateType, regex, specify, msg, objectKey, String.valueOf(object), fieldTypeEnum);
        } else if (object instanceof Double || object instanceof Integer) {
            //浮點
            jsonObjectList = validateDoubleByValidateType(validateType, specify, msg, objectKey, Integer.valueOf(String.valueOf(object)));
        }
        return jsonObjectList;
    }

    /**
     * map类型 判断
     *
     * @param validateType
     * @param regex
     * @param specify
     * @param msg
     * @param objectKey
     * @param oMap
     * @param jsonObjectList
     * @return
     */
    private List<JSONObject> checkByTypeMap(Class paramType, ValidateType validateType, String regex, int specify, String msg, String objectKey, Map oMap, List<JSONObject> jsonObjectList, FieldTypeEnum fieldTypeEnum) {
        Iterator entries = oMap.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry entry = (Map.Entry) entries.next();
            String key = String.valueOf(entry.getKey());
            objectKey += "-" + key;
            jsonObjectList.addAll(checkByType(paramType, validateType, regex, specify, msg, objectKey, entry.getValue(), jsonObjectList, fieldTypeEnum));
        }
        return jsonObjectList;
    }

    /**
     * list类型 判断
     *
     * @param validateType
     * @param regex
     * @param specify
     * @param msg
     * @param objectKey
     * @param oList
     * @param jsonObjectList
     * @return
     */
    private List<JSONObject> checkByTypeType(Class paramType, ValidateType validateType, String regex, int specify, String msg, String objectKey, List oList, List<JSONObject> jsonObjectList, FieldTypeEnum fieldTypeEnum) {
        for (Object object : oList) {
            objectKey += "->";
            jsonObjectList.addAll(checkByType(paramType, validateType, regex, specify, msg, objectKey, object, jsonObjectList, fieldTypeEnum));
        }
        return jsonObjectList;
    }


    /**
     * 根据验证类型进行验证(字符串)
     *
     * @param validateType
     * @param regex
     * @param specify
     * @param msg
     * @param o
     * @return
     */
    private List<JSONObject> validateStringByValidateType(ValidateType validateType, String regex, int specify, String msg, String oKey, String o, FieldTypeEnum fieldTypeEnum) {
        List<JSONObject> instructionsList = new ArrayList<>();
        //类型为 判断最大值或者判斷最小值
        if (validateType.equals(ValidateType.MAX_LENGTH) || validateType.equals(ValidateType.MIN_LENGTH)) {
            boolean checkNumLength = true;
            if (String.valueOf(FieldTypeEnum.NUMBER).equals(fieldTypeEnum.toString())) {
                //如果指定數字類型，则需要强转
                try {
                    double od = Double.parseDouble(o);
                    checkNumLength = specificValidationAdapter.checkNumLength(validateType, specify, od);
                } catch (NumberFormatException e) {
                    instructionsList.add(specificValidationAdapter.returnCustomInstructionsFull(validateType, Constant.NUMBERFORMAT_MSG, specify, oKey));
                }
            } else {
                checkNumLength = specificValidationAdapter.checkNumLength(validateType, specify, o.length());
            }
            if (!checkNumLength) {
                instructionsList.add(specificValidationAdapter.returnCustomInstructionsFull(validateType, msg, specify, oKey));
            }
        }
        //类型为 不能空
        if (validateType.equals(ValidateType.NOT_NULL) && StringX.isNotEmpty(o)) {
            instructionsList.add(specificValidationAdapter.returnCustomInstructionsFull(validateType, msg, specify, oKey));
        }

        //类型为 必须为空
        if (validateType.equals(ValidateType.NULL) && StringX.isEmpty(o)) {
            instructionsList.add(specificValidationAdapter.returnCustomInstructionsFull(validateType, msg, specify, oKey));
        }

        //类型为 正则表达式
        if (validateType.equals(ValidateType.REGEX)) {
            boolean checkRegex = specificValidationAdapter.checkRegex(regex, o);
            if (!checkRegex) {
                instructionsList.add(specificValidationAdapter.returnCustomInstructionsFull(validateType, msg, specify, oKey, regex));
            }
        }
        return instructionsList;
    }

    /**
     * 根据验证类型进行验证(带小数)
     *
     * @param validateType
     * @param specify
     * @param msg
     * @param o
     * @return
     */
    private List<JSONObject> validateDoubleByValidateType(ValidateType validateType, int specify, String msg, String oKey, double o) {
        List<JSONObject> instructionsList = new ArrayList<>();

        //类型为 判断最大值或者判斷最小值
        if (validateType.equals(ValidateType.MAX_LENGTH) || validateType.equals(ValidateType.MIN_LENGTH)) {
            boolean checkNumLength = specificValidationAdapter.checkNumLength(validateType, specify, o);
            if (!checkNumLength) {
                instructionsList.add(specificValidationAdapter.returnCustomInstructionsFull(validateType, msg, specify, oKey));
            }
        }
        return instructionsList;
    }

}
